__author__ = 'YT'

import os
import json
from abc import abstractmethod
import pygame as pg
from . import constants as c

class State():
    def __init__(self):
        self.start_time = 0.0
        self.current_time = 0.0
        self.done = False
        self.next = None
        self.persist = {}
    
    @abstractmethod
    def startup(self, current_time, persist):
        '''abstract method'''

    def cleanup(self):
        self.done = False
        return self.persist
    
    @abstractmethod
    def update(self, surface, keys, current_time):
        '''abstract method'''

class Control():
    def __init__(self):
        self.screen = pg.display.get_surface()
        self.done = False
        self.clock = pg.time.Clock()
        self.fps = 60
        self.keys = pg.key.get_pressed()
        self.mouse_pos = None
        self.current_time = 0.0
        self.state_dict = {}
        self.state_name = None
        self.state = None
        self.paused = False # 暂停状态
        self.game_info = {c.CURRENT_TIME:0.0,
                          c.LEVEL_NUM:1}
 
    def setup_states(self, state_dict, start_state):
        # 定义一个setup_states方法，用于初始化状态字典和开始状态
        self.state_dict = state_dict
        self.state_name = start_state
        self.state = self.state_dict[self.state_name]
        self.state.startup(self.current_time, self.game_info)

    def update(self):
        if not self.paused: # 如果没有暂停
            # 获取当前时间
            self.current_time = pg.time.get_ticks()
            # 如果当前状态已经完成，翻转状态
            if self.state.done:
                self.flip_state()
            # 更新当前状态
            self.state.update(self.screen, self.current_time, self.mouse_pos)
            # 将鼠标位置设置为None
            self.mouse_pos = None
    
    def flip_state(self):
        # 保存当前状态信息
        previous, self.state_name = self.state_name, self.state.next
        # 清理当前状态
        persist = self.state.cleanup()
        # 如果下一个状态存在，则切换状态
        if self.state_name in self.state_dict:
            self.state = self.state_dict[self.state_name]
        # 启动下一个状态
        self.state.startup(self.current_time, persist)

    def event_loop(self):
        # 遍历所有事件
        for event in pg.event.get():
            # 如果事件类型是退出
            if event.type == pg.QUIT:
                # 设置退出标志为True
                self.done = True
            # 如果事件类型是按键
            elif event.type == pg.KEYDOWN:
                # 获取按键状态
                self.keys = pg.key.get_pressed()
                if event.key == pg.K_SPACE:
                    self.paused = not self.paused
            # 如果事件类型是松开按键
            elif event.type == pg.KEYUP:
                # 获取按键状态
                self.keys = pg.key.get_pressed()
            # 如果事件类型是鼠标按键
            elif event.type == pg.MOUSEBUTTONDOWN:
                # 获取鼠标位置
                self.mouse_pos = pg.mouse.get_pos()

    def main(self):
        while not self.done:
            self.event_loop()
            self.update()
            pg.display.update()
            self.clock.tick(self.fps)
        print('game over')

def get_image(sheet, x, y, width, height, colorkey, scale):
        # 创建一个空pg.Surface对象，大小为width和height
        image = pg.Surface([width, height])
        # 获取image的矩形
        rect = image.get_rect()

        # 将sheet中的像素复制到image中，位置为(0, 0)，大小为(x, y, width, height)
        image.blit(sheet, (0, 0), (x, y, width, height))
        # 设置image的透明色为colorkey
        image.set_colorkey(colorkey)
        # 将image缩放到指定大小，宽高乘以scale
        image = pg.transform.scale(image,
                                   (int(rect.width*scale),
                                    int(rect.height*scale)))
        # 返回image
        return image

# 加载目录中的所有图形
def load_all_gfx(directory, colorkey=c.WHITE, accept=('.png', '.jpg', '.bmp', '.gif')):
    # 创建一个空字典来存储图形
    graphics = {}
    # 遍历目录中的所有文件
    for pic in os.listdir(directory):
        # 获取文件名和扩展名
        name, ext = os.path.splitext(pic)
        # 如果扩展名在accept中
        if ext.lower() in accept:
            # 加载图形
            img = pg.image.load(os.path.join(directory, pic))
            # 如果图形有透明通道
            if img.get_alpha():
                # 转换为透明图形
                img = img.convert_alpha()
            else:
                # 转换为255白色图形
                img = img.convert()
                # 设置颜色键
                img.set_colorkey(colorkey)
            # 将图形添加到字典中
            graphics[name] = img
    # 返回图形字典
    return graphics

def load_map_grid_image():
    # 定义一个空字典，用于存放地图格子图片
    grid_images = {}
    # 定义一个字典，用于存放图片的坐标
    image_rect_dict = {c.MAP_STONE:(80, 48, 16, 16), c.MAP_GRASS:(80, 32, 16, 16)}
    # 遍历字典，获取图片的坐标
    for type, rect in image_rect_dict.items():
        # 调用get_image函数，获取地图格子图片，并将其存入字典
        grid_images[type] = get_image(GFX['tile'], *rect, c.BLACK, 3)
    # 返回地图格子图片字典
    return grid_images

# 定义一个函数load_entiry_attr，用于加载文件中的属性
def load_entiry_attr(file_path):
    # 创建一个空字典attrs，用于存储属性
    attrs = {}
    # 打开文件
    f = open(file_path)
    # 读取文件中的数据
    data = json.load(f)
    # 关闭文件
    f.close()
    # 遍历data中的每一个键值对
    for name, attr in data.items():
        # 将键值对存入attrs字典中
        attrs[name] = attr
    # 返回attrs字典
    return attrs

def getMovePositions(x, y):
    # 如果地图是六边形
    if c.MAP_HEXAGON:
        # 如果y是偶数
        if y % 2 == 0:
            # 偏移量
            offsets = [(-1, 0), (-1, -1), (0, -1), (1, 0), (-1, 1), (0, 1)]
        # 如果y是奇数
        else:
            # 偏移量
            offsets = [(-1, 0), (0, -1), (1, -1), (1, 0), (0, 1), (1, 1)]
    # 如果地图是矩形
    else:
        # 使用四向或八向移动
        offsets = [(-1,0), (0, -1), (1, 0), (0, 1)]
        #offsets = [(-1,0), (0, -1), (1, 0), (0, 1), (-1,-1), (1, -1), (-1, 1), (1, 1)]
    # 返回偏移量
    return offsets

def getAttackPositions(x, y):
    '''获得攻击位置'''
    if c.MAP_HEXAGON:
        '''如果地图是六边形'''
        return getMovePositions(x, y)
    else:
        '''如果地图不是六边形'''
        return [(-1,-1), (-1,0), (-1,1), (0,-1), (0,1), (1,-1),(1,0), (1,1)]


# 定义一个函数，用于判断两个实体是否相邻
def isNextToEntity(entity1, entity2):
    # 如果地图是六边形
    if c.MAP_HEXAGON:
        # 获取entity1可以移动的位置列表
        dir_list = getMovePositions(entity1.map_x, entity1.map_y)
        # 遍历每一个位置
        for offset_x, offset_y in dir_list:
            # 计算出新的坐标
            x, y = entity1.map_x + offset_x, entity1.map_y + offset_y
            # 如果新的坐标与entity2的坐标相同，则返回True
            if x == entity2.map_x and y == entity2.map_y:
                return True
    # 如果地图是矩形
    else:
        # 如果entity1与entity2的横坐标之差和纵坐标之差都小于等于1，则返回True
        if abs(entity1.map_x - entity2.map_x) <= 1 and abs(entity1.map_y - entity2.map_y) <= 1:
            return True
    # 如果都不满足，则返回False
    return False

def getHexMapPos(x, y):
    # 定义一个函数getHexMapPos，用于计算给定坐标(x, y)在六边形网格中的位置
    X_LEN = c.HEX_X_SIZE // 2
    Y_LEN = c.HEX_Y_SIZE // 2
    # 计算x和y方向上单个六边形边长的一半
    if y % 2 == 0:
        # 如果y是偶数
        base_x = X_LEN * 2 * x
        # 计算x方向的基准位置
        base_y = Y_LEN * 3 * (y//2)
        # 计算y方向的基准位置
    else:
        base_x = X_LEN * 2 * x + X_LEN
        # 如果y是奇数，x方向的基准位置需要加上一个边长的一半
        base_y = Y_LEN * 3 * (y//2) + Y_LEN//2 + Y_LEN
        # y方向的基准位置需要加上一个边长的一半加上一个边长
    return (base_x, base_y)
    # 返回计算得到的x和y方向的基准位置

class Vector2d():
    # 初始化向量
    def __init__(self, x, y):
        self.x = x
        self.y = y

    # 减去另一个向量
    def minus(self, vec):
        return Vector2d(self.x - vec.x, self.y - vec.y)

    # 计算叉乘
    def crossProduct(self, vec):
        return (self.x * vec.y - self.y * vec.x)

def isInTriangle(x1, y1, x2, y2, x3, y3, x, y):
    # 定义一个函数，判断点(x, y)是否在三角形(x1, y1), (x2, y2), (x3, y3)内部
    A = Vector2d(x1, y1)
    B = Vector2d(x2, y2)
    C = Vector2d(x3, y3)
    P = Vector2d(x, y)
    # 计算向量
    PA = A.minus(P)
    PB = B.minus(P)
    PC = C.minus(P)
    # 计算叉乘
    t1 = PA.crossProduct(PB)
    t2 = PB.crossProduct(PC)
    t3 = PC.crossProduct(PA)
    # 如果t1和t2符号相同，且t1和t3符号相同，则点在三角形内部
    if (t1 * t2 >= 0) and (t1 * t3 >= 0):
        return True
    return False

def getHexMapIndex(x, y):
    # 定义X_LEN和Y_LEN变量，分别表示x和y轴的长度
    X_LEN = c.HEX_X_SIZE // 2
    Y_LEN = c.HEX_Y_SIZE // 2
    # 定义tmp_x和offset_x变量，表示x轴的值和偏移量
    tmp_x, offset_x = divmod(x, c.HEX_X_SIZE)
    # 定义tmp_y和offset_y变量，表示y轴的值和偏移量
    tmp_y, offset_y = divmod(y, Y_LEN * 3)
    # 定义map_x和map_y变量，表示最终计算得到的x和y轴的值
    map_x, map_y = 0, 0
    # 判断offset_y的值，如果小于等于Y_LEN+Y_LEN//2，则进行判断
    if offset_y <= (Y_LEN + Y_LEN//2):
        # 如果offset_y大于等于Y_LEN//2，则map_x和map_y等于tmp_x和tmp_y乘以2
        if offset_y >= Y_LEN//2:
            map_x, map_y = tmp_x, tmp_y * 2
        # 否则，定义一个三角形列表，表示三个三角形
        else:
            triangle_list = [(0, 0, 0, Y_LEN//2, X_LEN, 0),
                             (0, Y_LEN//2, X_LEN, 0, c.HEX_X_SIZE, Y_LEN//2),
                             (X_LEN, 0, c.HEX_X_SIZE, 0, c.HEX_X_SIZE, Y_LEN//2)]
            # 定义一个map_list列表，表示三个点的坐标
            map_list = [(tmp_x - 1, tmp_y * 2 -1), (tmp_x, tmp_y * 2), (tmp_x, tmp_y * 2 -1)]
            # 遍历三角形列表，判断offset_x和offset_y是否在三角形内
            for i, data in enumerate(triangle_list):
                if isInTriangle(*data, offset_x, offset_y):
                    # 如果offset_x和offset_y在三角形内，则map_x和map_y等于map_list中的对应坐标
                    map_x, map_y = map_list[i]
                    break
    elif offset_y >= c.HEX_Y_SIZE:
        # 如果偏移量offset_y大于等于HEX_Y_SIZE，则判断offset_x的值
        if offset_x <= X_LEN:
            # 如果offset_x小于等于X_LEN，则map_x为tmp_x - 1，map_y为tmp_y * 2 + 1
            map_x, map_y = tmp_x - 1, tmp_y * 2 + 1
        else:
            # 如果offset_x大于X_LEN，则map_x为tmp_x，map_y为tmp_y * 2 + 1
            map_x, map_y = tmp_x, tmp_y *2 + 1
    else:
        # 如果偏移量offset_y小于HEX_Y_SIZE，则判断offset_x和offset_y所在的三角形区域
        triangle_list = [(0, Y_LEN + Y_LEN//2, 0, c.HEX_Y_SIZE, X_LEN, c.HEX_Y_SIZE),
                         (0, Y_LEN + Y_LEN//2, X_LEN, c.HEX_Y_SIZE, c.HEX_X_SIZE, Y_LEN + Y_LEN//2),
                         (X_LEN, c.HEX_Y_SIZE, c.HEX_X_SIZE, Y_LEN + Y_LEN//2, c.HEX_X_SIZE, c.HEX_Y_SIZE)]
        map_list = [(tmp_x - 1, tmp_y * 2 + 1), (tmp_x, tmp_y * 2), (tmp_x, tmp_y *2 + 1)]
        for i, data in enumerate(triangle_list):
            # 遍历三角形区域，判断offset_x和offset_y是否在当前三角形区域内
            if isInTriangle(*data, offset_x, offset_y):
                map_x, map_y = map_list[i]
                break
    if map_x == 0 and map_y == 0:
        # 如果map_x和map_y仍然为0，则打印相关信息
        print('pos[%d, %d](%d, %d) base[%d, %d] off[%d, %d] ' % (map_x, map_y, x, y, tmp_x, tmp_y, offset_x, offset_y))
    return (map_x, map_y)

pg.init()
# 设置窗口标题
pg.display.set_caption(c.ORIGINAL_CAPTION)
# 设置窗口大小
SCREEN = pg.display.set_mode(c.SCREEN_SIZE)

# 加载所有图形
GFX = load_all_gfx(os.path.join("resources","graphics"))
# 加载实体属性
ATTR = load_entiry_attr(os.path.join('source', 'data', 'entity.json'))
# 加载地图网格图像
GRID = load_map_grid_image()